<?php
/**
 * The MIT License
 *
 * Copyright (c) <year> <copyright holders>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @author Damiano Ciarla
 * @package plugin
 * @subpackage entity_manager/class/abstract
 */
abstract class EntityManager_Abstract_JoinManager
{
	/**
	 * Istanza del gestore delle funzioni
	 *
	 * @var mixed
	 */
	public $_functionInstance;

	/**
	 * Lista delle tabelle da mettere in join
	 *
	 * @var array|EntityManager_Join_Find
	 */
	protected $_tables = array();

	/**
	 * Metodo che esegue il controllo sulla validità della JOIN che si sta effettuando
	 * verificando l'esistenza della relazione tra le due entità interessate e verificando
	 * se un eventuale "customJoin" è stata scritta correttamente specificando i campi
	 * reali presenti nelle tabelle
	 *
	 * @param EntityManager_Abstract_Entity $currentEntity Istanza della entity corrente
	 * @param string $previusEntity Nome della entity a cui agganciare la join
	 * @param string $customJoin Campi su cui effettuare la join
	 *
	 * @return boolean
	 */
	protected function _checkPreviusEntity(EntityManager_Abstract_Entity $currentEntity, $previusEntity = null, $customJoin = null)
	{
		// Variabile di controllo
		$entityFound = false;

		// Verifico se sono state specificate entity a cui agganciare la join. Se non vengono specificate
		// entity precedenti viene effettuato il controllo tra quella corrente e la precedente
		if($previusEntity === null)
		{
			$entityFound = true;

			// Imposto la entity precedente prendendo quella principale o l'ultima inserita
			$previusEntity = ((!count($this->_tables)) ? $this->_functionInstance->_getEntity() : end($this->_tables)->_getEntity());
			// Verifico che la entity corrente può effettuare una join con le entity precedente
			$this->_checkPreviusEntity_checkAutomaticField($previusEntity, $currentEntity, &$customJoin);
		}
		else
		{
			// Inizio la verifica della presenza della entity sul quale effettuare la join cercando
			// inizialmente la corrispondenza con la entity princiaple e successivamente con la lista
			// di quelle già presenti in join

			// Verifico la compatibilità del valore dell'entità da mettere in join con quella iniziale da cui si è partiti
			if($this->_checkPreviusEntity_checkClassName($this->_functionInstance->_getEntity(), $previusEntity))
			{
				$entityFound = true;
				$previusEntity = $this->_functionInstance->_getEntity();

				// Verifico se è stato valorizzato il valore che esplicita il nome dei campi su cui effettuare la join
				if($customJoin !== null)
				{
					// Eseguo il controllo per verificare se i campi sono stati scritti correttamente e se appartengono ad entrambe le entity
					$this->_checkPreviusEntity_checkCustomField($previusEntity, $currentEntity, &$customJoin);
				}
				else
				{
					// Verifico che la entity corrente può effettuare una join con le entity principale
					$this->_checkPreviusEntity_checkAutomaticField($previusEntity, $currentEntity, &$customJoin);
				}
			}
			else
			{
				// La entity a cui agganciare la join non corrisponde alla principale e mi preparo a ciclare
				// le eventuali antity aggiunte successivamente

				// Ciclo tutte le entity se presenti per verifica la corrispondenza con quella esplicitata nel parametro del metodo
				foreach($this->_tables as $table)
				{
					// Verifico la compatibilità sul nome dell'istanza della entity
					if($this->_checkPreviusEntity_checkClassName($table->_getEntity(), $previusEntity))
					{
						$entityFound = true;
						$previusEntity = $table->_getEntity();

						// Verifico se è stato valorizzato il valore che esplicita il nome dei campi su cui effettuare la join
						if($customJoin !== null)
						{
							// Eseguo il controllo per verificare se i campi sono stati scritti correttamente e se appartengono ad entrambe le entity
							$this->_checkPreviusEntity_checkCustomField($previusEntity, $currentEntity, &$customJoin);
						}
						else
						{
							// Verifico che la entity corrente può effettuare una join con le entity che si sta analizzando
							$this->_checkPreviusEntity_checkAutomaticField($previusEntity, $currentEntity, &$customJoin);
						}

						break;
					}
				}
			}
		}

		return $entityFound;
	}

	/**
	 * Metodo che verifica se il nome della entity è lo stesso della entity su cui
	 * viene effettuato il controllo
	 *
	 * @param EntityManager_Abstract_Entity $previusEntity Istanza della entity
	 * @param string $previusEntityName Nome dell'istanza della entity
	 *
	 * @return boolean
	 */
	protected function _checkPreviusEntity_checkClassName(EntityManager_Abstract_Entity $previusEntity, $previusEntityName)
	{
		$className = get_class($previusEntity);
		$className = ((strpos($className, "Base") === false) ? $className : substr($className, 4));

		if($className == $previusEntityName)
			return true;

		return false;
	}

	/**
	 * Metodo che verifica se due entity possono eseguire una join
	 *
	 * @param EntityManager_Abstract_Entity $previusEntity Istanza della entity
	 * @param EntityManager_Abstract_Entity $currentEntity Istanza della entity
	 */
	protected function _checkPreviusEntity_checkAutomaticField(EntityManager_Abstract_Entity $previusEntity, EntityManager_Abstract_Entity $currentEntity, $customJoin = null)
	{
		// Recupero le informazioni della entity su cui agganciare quella corrente
		$previusEntityProperties = $previusEntity->getProperty();
		// Recupero le informazioni della entity corrente
		$currentEntityProperties = $currentEntity->getProperty();

		$checkJoin = false;

		// Lista delle join
		$listJoin = array();

		if(isset($previusEntityProperties['ENTITY']['JOIN_TO']))
			$listJoin = array_merge($listJoin, $previusEntityProperties['ENTITY']['JOIN_TO']);

		if(isset($previusEntityProperties['ENTITY']['JOIN_FROM']))
			$listJoin = array_merge($listJoin, $previusEntityProperties['ENTITY']['JOIN_FROM']);

		// Ciclo tutte le join trovate per verificare se esiste la relazione tra del due entità
		foreach($listJoin as $infoJoinTo)
		{
			// Verifico se la tabella presente nella join corrisponde alla tabella corrente
			if($infoJoinTo['JOIN']['TABLE'] == $currentEntityProperties['ENTITY']['TABLE'])
			{
				// Costruisco il codice della join
				$customJoin = $previusEntityProperties['ENTITY']['TABLE'] . "." . $infoJoinTo['FIELD'] . " = " . $infoJoinTo['JOIN']['TABLE'] . "." . $infoJoinTo['JOIN']['FIELD'];
				// Imposto la variabile di controllo come positiva per evitare di lanciare l'eccezzione
				$checkJoin = true;

				break;
			}
		}

		if(!$checkJoin)
		{
			// Recupero il nome dell'istanza della entity precedente
			$classNamePrevius = get_class($previusEntity);
			$classNamePrevius = ((strpos($classNamePrevius, "Base") === false) ? $classNamePrevius : substr($classNamePrevius, 4));

			// Recupero il nome dell'istanza della entity corrente
			$classNameCurrent = get_class($currentEntity);
			$classNameCurrent = ((strpos($classNameCurrent, "Base") === false) ? $classNameCurrent : substr($classNameCurrent, 4));

			/**
			 * @see EntityManager_Exception::NO_RELATIONSHIP_WITH_PREVIUS_ENTITY
			 */
			throw new EntityManager_Exception(array($classNamePrevius, $classNameCurrent), EntityManager_Exception::NO_RELATIONSHIP_WITH_PREVIUS_ENTITY);
		}
	}

	/**
	 * Metodo che controlla se la relazione esplicitata sia corretta sotto l'aspetto
	 * del nome delle tabelle e dei rispettivi campi
	 *
	 * @param EntityManager_Abstract_Entity $previusEntity Istanza della entity precedente
	 * @param EntityManager_Abstract_Entity $currentEntity Istanza della entity corrente
	 * @param string $condition Valore della condizione da applicare alla relazione
	 */
	protected function _checkPreviusEntity_checkCustomField(EntityManager_Abstract_Entity $previusEntity, EntityManager_Abstract_Entity $currentEntity, $condition)
	{
		$checkCondition = preg_match('/(.*)=(.*)/', $condition, $matches);

		if(count($matches) == 3)
		{
			$previusEntityProperties = $previusEntity->getProperty();
			$currentEntityProperties = $currentEntity->getProperty();

			$previusTableData = explode(".", trim($matches[1]));
			$currentTableData = explode(".", trim($matches[2]));

			if($previusEntityProperties['ENTITY']['TABLE'] != $previusTableData[0])
			{
				// ECCEZIONE NOME TABELLA NON VALIDA
			}

			if(!array_key_exists($previusTableData[1], $previusEntityProperties['FIELDS']))
			{
				// ECCEZIONE NOME CAMPO NON VALIDO
			}

			if($currentEntityProperties['ENTITY']['TABLE'] != $currentTableData[0])
			{
				// ECCEZIONE NOME TABELLA NON VALIDA
			}

			if(!array_key_exists($currentTableData[1], $currentEntityProperties['FIELDS']))
			{
				// ECCEZIONE NOME CAMPO NON VALIDO
			}
		}
		else
		{
			// ECCEZIONE CONDIZIONE CON VALIDA
		}
	}

	/**
	 * Metodo che restituisce la lista degli oggetti EntityManager_Join_Find dove sono
	 * contenute le informazioni per ogni tabella da mettere in join
	 *
	 * @return array|EntityManager_Join_Find
	 */
	public function getTables()
	{
		return $this->_tables;
	}
}
?>
